/*
Promise源码模拟
实现思路：
1 Promise是一个类，需要传递一个方法（执行器），通过new关键字创建Promise的时候，执行器会立即执行
2 Promise执行器中接收两个函数，这两个函数式用来改变Promise的状态的
3 Promise的状态分为三种：pending（等待）|fulfilled（成功）|rejected (失败)，状态只能由pending变成fulfilled或者rejected,状态一旦改为成功或者失败则不可再更改了
4 Promise中, 可以用一个变量来接收new Promise的结果，这个变量有一个then方法，then方法中接收两个函数，根据不同的状态（成功或失败）来调用相应的函数
5 then方法中的成功回调函数用一个参数，该参数表示成功之后返回的值， then方法中还有一个失败的回调函数，失败回调中同样也有一个参数，表示失败的原因
6 then 方法可以多次调用，当执行器中存在异步代码，且多次调用了then方法，则要考虑将then方法传递的回调函数用数组的方式保存起来
7 Promise的then支持链式调用, 链式调用中then方法的成功回调中的值取决于上一个then方法中return的返回值
8 Promise的then方法中不能返回上一次调用Promise对象的返回值，否则会报错 TypeError: Chaining cycle detected for promise #<Promise>
9 Promise中的执行器中的错误以及then方法的回调中发生的错误都是能够捕获到的，并且在then方法的失败回调中可以拿到具体的错误信息
10 Promise的then方法是可选的, 当then没有参数的时候，Promise内部会把执行器中的值向后传递，直到传递到有参数的then方法中
11 Promise中存在一个静态的all方法，这个参数接收一个数组作为参数，这个方法的返回值依然是一个Promise,可以通过then方法拿到成功的返回结果，成功的返回结果也是一个数组，当Promsie的all方法中传递的所有参数都执行成功了，才会执行then方法的回调参数，如果有一个失败了则会立即执行then方法中的失败回调
  Promise的all方法允许我们按照异步调用的顺序，得到异步代码执行的结果
12 Promise中有一个静态的resolve方法，可以将给定的值转换成Promise对象
13 Promise中有一个finally方法，这个finally有两个特别，无论当前的Promise对象的状态是成功的还是失败的，finally都会执行，且finally后面可以继续链式调用then方法来拿到Promise中的返回结果
14 Promise中有一个catch方法，可以捕获到Promise失败状的具体错误信息
*/
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  // 执行器 executor
  constructor(executor) {
    try {
      //立即执行
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  //记录MyPromise的状态
  status = PENDING

  // 保存成功之后的值
  value = undefined

  //保存失败的原因
  reason = undefined

  // 保存成功回调
  sucessCallBack = []

  // 保存失败的回调
  failCallBack = []

  resolve = (value) => {
    // 将状态更改为成功
    if (this.status !== PENDING) return // 状态只能由pending改为fulfilled
    this.status = FULFILLED
    this.value = value
    // this.sucessCallBack && this.sucessCallBack(this.value)
    //循环执行sucessCallBack中的方法
    while (this.sucessCallBack.length) this.sucessCallBack.shift()()
  }

  reject = (reason) => {
    // 将状态更改为成功
    if (this.status !== PENDING) return // 状态只能由pending改为rejected
    this.status = REJECTED
    this.reason = reason
    // this.failCallBack && this.failCallBack(this.reason)
    //循环执行failCallBack中的方法
    while (this.failCallBack.length) this.failCallBack.shift()()
  }

  then(sucessCallBack, failCallBack) {
    sucessCallBack = sucessCallBack ? sucessCallBack : (value) => value
    failCallBack = failCallBack ? failCallBack : (err) => err
    //返回MyPromise对象，以支持then方法的链式调用
    const promise = new MyPromise((resolve, reject) => {
      //判断MyPromise的状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          try {
            // 成功之后执行成功回调
            let res = sucessCallBack(this.value)
            resolvePromise(promise, res, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            // 失败之后执行失败回调
            let err = failCallBack(this.reason)
            resolvePromise(promise, err, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else {
        // 保存回调，以支持异步代码
        this.sucessCallBack.push(() => {
          setTimeout(() => {
            try {
              // 成功之后执行成功回调
              let res = sucessCallBack(this.value)
              resolvePromise(promise, res, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
        this.failCallBack.push(() => {
          setTimeout(() => {
            try {
              // 失败之后执行失败回调
              let err = failCallBack(this.reason)
              resolvePromise(promise, err, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        })
      }
    })
    return promise
  }
  finally(calllBack) {
    return this.then(
      (res) => {
        return MyPromise.resolve(calllBack()).then(()=>res)
      },
      (err) => {
        return MyPromise.resolve(calllBack()).then(()=>{throw err})
      }
    )
  }
  catch(failCallBack){
    return this.then(undefined, failCallBack)
  }
  static all(arr) {
    // result用于存放结果集
    let result = []
    // index用户计算all方法中的异步代码是否都执行完毕
    let index = 0
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        result[key] = value
        index++
        if (index === arr.length) {
          resolve(result)
        }
      }
      for (let i = 0; i < arr.length; i++) {
        // 判断all方法中传递过来的参数是MyPromise对象还是普通值
        let current = arr[i]
        if (current instanceof MyPromise) {
          // MyPromise对象
          current.then(
            (res) => addData(i, res),
            (err) => reject(err)
          )
        } else {
          // 普通值
          addData(i, current)
        }
      }
    })
  }
  static resolve(value) {
    //判断传递过来的值是普通值还是MyPromise对象
    if (value instanceof MyPromise) return value // MyPromise对象
    // 普通值
    return new MyPromise((resolve) => resolve(value))
  }
}
function resolvePromise(promise, x, resolve, reject) {
  //判断上一个then的执行结果是不 是本次then方法成功回调的返回值
  if (promise === x) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>')
    )
  }
  // 判断res的值是普通值还是MyPromise对象
  // 如果是普通值，则直接返回给下一个then方法
  // 如果是MyPromise对象，根据MyPromise返回的结果来觉得调用resolve还是reject
  if (x instanceof MyPromise) {
    // MyPromise对象
    // x.then((res)=>{resolve(res)},err=>reject(err))
    x.then(resolve, reject)
  } else {
    // 普通值
    resolve(x)
  }
}
module.exports = MyPromise
